package com.nl.swagger;

import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import com.github.xiaoymin.knife4j.spring.filter.SecurityBasicAuthFilter;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.util.CollectionUtils;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

/**
 * swagger自动配置.
 *
 * @author zenghao
 */

@Configuration
@EnableKnife4j
@Profile({"dev", "test"})
@EnableConfigurationProperties(SwaggerProperties.class)
@ConditionalOnBean(annotation = EnableSwagger2WebMvc.class)
public class SwaggerAutoConfiguration {

    @Autowired
    private SwaggerProperties swaggerProperties;

    /**
     * 默认文档对象.
     *
     * @return 文档对象
     */
    @Bean
    @ConditionalOnMissingBean(Docket.class)
    public Docket swaggerDocket() {
        return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(buildApiInfo())
            .select()
            .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
            .paths(this::excludePathPredicate).build()
            .securityContexts(buildSecurityContexts())
            .securitySchemes(buildApiKeys());
    }

    /**
     * 文档认证过滤器.
     *
     * @return 文档认证过滤器
     */
    @Bean
    @ConditionalOnMissingBean(SecurityBasicAuthFilter.class)
    public SecurityBasicAuthFilter securityBasicAuthFilter() {
        SwaggerProperties.Auth auth = swaggerProperties.getAuth();
        return new SecurityBasicAuthFilter(auth.isEnabled(), auth.getUsername(), auth.getPassword());
    }

    /**
     * 排除路径.
     *
     * @param path 接口路径
     * @return 判断路径是否需要排除
     */
    private boolean excludePathPredicate(final String path) {
        Predicate<String> exclude = PathSelectors.any();
        if (!CollectionUtils.isEmpty(swaggerProperties.getExcludePaths())) {
            return swaggerProperties.getExcludePaths().stream().map(PathSelectors::ant).anyMatch(fn -> fn.test(path));
        }
        return exclude.test(path);
    }

    /**
     * 构建接口说明.
     *
     * @return 接口说明
     */
    private ApiInfo buildApiInfo() {
        return new ApiInfoBuilder()
            .title(swaggerProperties.getTitle())
            .description(swaggerProperties.getDescription())
            .license(swaggerProperties.getLicense())
            .licenseUrl(swaggerProperties.getLicenseUrl())
            .version(swaggerProperties.getVersion())
            .contact(new Contact(swaggerProperties.getContactName(), swaggerProperties.getContactUrl(), swaggerProperties.getContactEmail()))
            .build();
    }

    /**
     * 构建api全局key.
     *
     * @return api全局key
     */
    private List<ApiKey> buildApiKeys() {
        List<ApiKey> result = new ArrayList<>();
        result.add(new ApiKey("Authorization", "Authorization", "header"));
        if (!CollectionUtils.isEmpty(swaggerProperties.getApiKeys())) {
            swaggerProperties.getApiKeys().forEach(o -> {
                result.add(new ApiKey(o.getName(), o.getKeyname(), o.getPassAs()));
            });
        }
        return result;
    }

    /**
     * 构建接口安全上下文.
     *
     * @return 接口安全上下文
     */
    private List<SecurityContext> buildSecurityContexts() {
        List<SecurityContext> result = new ArrayList<>();
        SecurityContext sc = SecurityContext.builder()
            .securityReferences(defaultAuth())
            .forPaths(PathSelectors.regex("/.*"))
            .build();

        result.add(sc);
        return result;
    }

    /**
     * 构建默认安全配置.
     *
     * @return 安全配置
     */
    private List<SecurityReference> defaultAuth() {
        List<SecurityReference> result = new ArrayList<>();
        AuthorizationScope[] authScopes = new AuthorizationScope[]{new AuthorizationScope("global", "全局设置")};

        buildApiKeys().forEach(o -> {
            result.add(new SecurityReference(o.getName(), authScopes));
        });
        return result;
    }
}
